home *** CD-ROM | disk | FTP | other *** search
/ The Arsenal Files 1 / The Arsenal Files (Arsenal Computer).ISO / novell / winup9.exe / VIPX.DOC < prev    next >
Text File  |  1993-12-16  |  32KB  |  924 lines

  1. VIPX.386 Description, Limitations and Configuration Document
  2.  
  3. Current Version:        1.17
  4. Date:                   931118
  5.  
  6. Table of Contents
  7. -----------------
  8. I.   Description
  9. II.  Limitations
  10.     A.  Windows 3.X Versions
  11.     B.  Version Compatibility with Dedicated IPX
  12.     C.  Packet Size Limitations
  13.     D.  Memory Manager Limitations
  14. III. System Settings in the SYSTEM.INI File
  15.     A.  [386Enh] Section
  16.         1.  TimerCriticalSection
  17.     B.  [VIPX] Section
  18.         1.  VipxMappingPages
  19.         2.  VipxFailOverSizedPackets
  20.         3.  VpicdFix
  21.         4.  AutoIrqVirtualize
  22.         5.  VirtualizeIrq[0-F]
  23.         6.  VipxErrorMessages
  24.         7.  VipxWarningMessages
  25.         8.  VipxOutDebugStrOnErrors
  26.         9.  VipxOutDebugStrOnWarnings
  27.         10. VipxBreakOnErrors
  28.         11. VipxBreakOnWarnings
  29. IV.  Programming to the VIPX Interface
  30.     A.  Getting the VIPX API Entry Point
  31.     B.  Memory Management Issues
  32.     C.  APIs Specific to VIPX.386
  33.     D.  Handling Reentrancy Issues
  34.     E.  Using NWIPXSPX.DLL
  35. V.   Global DOS TSRs using IPX/SPX
  36.  
  37.  
  38. I.  Description
  39. ---------------
  40.  
  41. VIPX.386 is a Windows 3.X virutalization driver for IPXODI.COM
  42. driver.  It virtualizes requests to the globally loaded IPX driver. 
  43. When a request is made to IPX, VIPX will allocate a request buffer in
  44. the system's global memory, copy the original request to the global
  45. buffer and give the global request to IPX.  When the global request
  46. completes, IPX will call VIPX.  VIPX will then copy any results back
  47. to the original request buffer and call the application which
  48. submitted the request.
  49.  
  50.  
  51. II.  Limitations
  52. ----------------
  53.  
  54.  
  55. II.A.  Windows Versions
  56. -----------------------
  57.  
  58. VIPX supports Windows versions 3.0, 3.0a and 3.1.  However, there is
  59. some functionality which VIPX will only support in Windows 3.1. This
  60. is LAN IRQ virtualization for avoiding deadlocks.  Under WIndows 3.0
  61. and 3.0a, VIPX will not virtualize LAN IRQs.  See section III.B.5
  62. VirtualizeIrq[0-F].
  63.  
  64. A patch has been developed by Microsoft for Windows 3.1.
  65. The patch is VTDA.386 found in WW0863.EXE on MicroSoft's BBS.  The
  66. BBS phone number is (206)936-6735.  Microsoft found a bug in the
  67. their Virtual Timer Device (VTD) driver which may cause a deadlock 
  68. with the NetWare shell (NETX). 
  69.  
  70. The deadlock occurs as a result of the shell waiting for the timer
  71. tick to advance.  When an NCP is sent, the shell will start it's
  72. retry timeout countdown.  This is essentially a loop waiting for the
  73. timer tick value to advance to a certain point.  The deadlock occurs
  74. when the tick value never increments and thus the shell is left in an
  75. infinite loop.  The reason the tick count is never incremented is
  76. that the timer interrupt is not allowed to be simulated into V86 mode
  77. under certain circumstances.  The patch to the VTD allows the timer
  78. interrupts to be simulated.
  79.  
  80.  
  81.  
  82. II.B.  Version Compatibility with Dedicated IPX
  83. -----------------------------------------------
  84.  
  85. Novell offically ceased maintenence on the dedicate IPX driver
  86. (IPX.OBJ) verion 3.10 dated 11-21-91.  The last version of VIPX to
  87. explicitly support that driver is 1.10.  The current version of VIPX
  88. supports IPXODI.COM only.  It is strongly recommended that you update
  89. your dedicated IPX driver with IPXODI.COM when using versions of VIPX
  90. later than 1.10.
  91.  
  92.  
  93. II.C.  Packet Size Limitations
  94. ------------------------------
  95.  
  96. VIPX.386 will only virtualize packets which are 8000 (decimal) bytes
  97. or less.  Any DOS and Windows IPX or SPX applications which use
  98. networks with a physical packet size greater than 8000 bytes may not
  99. work with VIPX.386.  For example, IBM Token Ring can be configured to
  100. run with 16K packets.  A request by a DOS or Windows IPX application
  101. to send a 16K packet will be truncated to 8000 bytes.  On the other
  102. hand, any 16K packet that is received by the LAN driver will be
  103. dropped because VIPX cannot allocate a packet big enough to handle
  104. it.
  105.  
  106.  
  107. II.D.  Memory Manager Limitations
  108. ---------------------------------
  109.  
  110. When an request is passed up from IPX, VIPX will immediately test the
  111. request buffer to see if it is in global memory already.  If it is in
  112. global memory, the request will be passed back down to IPX without
  113. any virtualization.  For example, a TSR loaded before Windows is
  114. considered to be in global memory.  If that TSR calls IPX, VIPX will
  115. test the requests and pass them back down to IPX.  This is done
  116. because there is no need to virtualize requests which are already
  117. global.
  118.  
  119. The use of UMB memory complicates the test for global memory.  There
  120. are two basic senarios.  Senario One is when a TSR has been loaded
  121. high before Windows was loaded.  In this case, IPX requests will come
  122. from global UMB memory.  VIPX will simply pass these requests back
  123. down to IPX.  Senario Two is when a TSR is loaded high in a Windows
  124. DOSBOX.  In this case, IPX requests will come from local UMB memory. 
  125. VIPX will virtualize these requests.
  126.  
  127. Using HIMEM.SYS and EMM386.SYS, all tests for global UMB memory will
  128. work properly under both of senarios.
  129.  
  130. However, there are some memory managers that do not work properly
  131. under Windows.  With these drivers, all LOCAL DOSBOX UMBs look as if
  132. they are GLOBAL UMBs.  Under Senario Two, when a TSR calls IPX, VIPX
  133. will test the request buffer and think that it is in global UMB
  134. memory when it is really in LOCAL UMB memory.  As a result, VIPX will
  135. pass a local ECB to IPX without virtualization.  The normal result of
  136. this is a hung machine or data corruption.
  137.  
  138.  
  139. III.  System Settings in the SYSTEM.INI File
  140. --------------------------------------------
  141.  
  142. III.A.  [386Enh] Section
  143. ------------------------
  144.  
  145. III.A.1.  TimerCriticalSection
  146. ------------------------------
  147.  
  148. As of version 1.15 of VIPX, TimerCriticalSection is required to be set
  149. on.  The recommended setting is as follows:
  150.  
  151. [386Enh]
  152. TimerCriticalSection=10000
  153.  
  154. The reason for this parameter is to avoid a deadlock with the LAN IRQ
  155. Virtualization code.  See section III.B.5 VirtualizeIrq[0-F].
  156.  
  157.  
  158. III.B.  [VIPX] Section
  159. ------------------------
  160.  
  161. Under most circumstances, VIPX will work fine under the default
  162. configuration.  However, there may be some applications which require
  163. custom configuration of the driver.  This is a list of SYSTEM.INI
  164. parameters which can be used to configure VIPX:
  165.  
  166. [VIPX]
  167. VipxMappingPages                =[number of 4K pages]   (default = 16)
  168. VipxFailOverSizedPackets        =[ON|OFF|TRUE|FALSE]    (default = OFF)
  169. VpicdFix                        =[ON|OFF|TRUE|FALSE]    (default = ON)
  170. AutoIrqVirtualize               =[ON|OFF|TRUE|FALSE]    (default = ON)
  171. VirtualizeIrq[0-F]              =[ON|OFF|TRUE|FALSE]    (default = OFF)
  172.  
  173. (BELOW ARE CONFIGURATION PARAMETERS FOR BETA VERSIONS ONLY!)
  174. VipxErrorMessages               =[ON|OFF|TRUE|FALSE]    (default = OFF)
  175. VipxWarningMessages             =[ON|OFF|TRUE|FALSE]    (default = OFF)
  176. VipxBreakOnErrors               =[ON|OFF|TRUE|FALSE]    (default = OFF)
  177. VipxBreakOnWarnings             =[ON|OFF|TRUE|FALSE]    (default = OFF)
  178. VipxOutDebugStrOnErrors         =[ON|OFF|TRUE|FALSE]    (default = OFF)
  179. VipxOutDebugStrOnWarnings       =[ON|OFF|TRUE|FALSE]    (default = OFF)
  180.  
  181.  
  182. III.B.1.  VipxMappingPages
  183. --------------------------
  184.  
  185. This is the number of pages that VIPX can use to globalize requests
  186. to the global IPXODI.COM driver.  VIPX is not absolutely guaranteed
  187. to have all of these pages available at any one point, since this is
  188. the requested number of pages for SHARED global mapping which VIPX
  189. makes to the Windows VMM at initialization time.
  190.  
  191.  
  192. III.B.2.  VipxFailOverSizedPackets
  193. ----------------------------------
  194.  
  195. This parameter tells VIPX to fail any requests which require more
  196. than the maximum allowed globalization size.  The actual maximum will
  197. vary according to which media the user is on.  The absolute maximum
  198. is 8000 (decimal) bytes.  With media that have smaller packets than
  199. 8000 bytes, the maximum allowed size is the maximum packet size that
  200. can be put onto the media.
  201.  
  202.  
  203. III.B.3.  VpicdFix
  204. ------------------
  205. III.B.4.  AutoIrqVirtualize
  206. ---------------------------
  207.  
  208. VpicdFix and AutoIrqVirtualize are synonyms for the same option.
  209. The VPICD VxD has a problem of allowing an interrupt to be simulated
  210. to a VM which previously held the Windows Critical Section but currently
  211. does not.  (For more details, see III.B.5. VirtualizeIrq[0-F]).  This 
  212. causes a deadlock situation when LAN I/O is attemped in another VM.  
  213. To get around the problem, VIPX will take over the job of virtualizing 
  214. LAN IRQs and will simulate LAN interrupts only to the owner of the 
  215. Windows Critical Section.
  216.  
  217. This option is used to turn on or off the automatic detection and
  218. virtualization of LAN IRQs when ODI LAN drivers or dedicated LAN
  219. drivers are loaded.  The reason for this option is to allow OEMs to 
  220. virtualize their LAN drivers with their own VxDs.
  221.  
  222.  
  223.  
  224. III.B.5.  VirtualizeIrq[0-F]
  225. ----------------------------
  226.  
  227. This is option is automatically configured excepted on machines using
  228. the IBM LAN Support Program.  This parameter tells VIPX to virtualize
  229. the specified (hex) Interrupt ReQuest (IRQ) line.  This feature is
  230. mainly designed to assist the IBM LAN Support Program in avoiding the
  231. "Black Screen of Death".
  232.  
  233. Because of a design flaw in the VPICD.386 driver, network interrupts
  234. will sometimes deadlock a PC running Windows 3.X.  VIPX 1.15 and later
  235. attempts to avoid the deadlock by virtualizing the Network Interface 
  236. Card's (NIC) IRQ(s).  With ODI and dedicated IPX (IPX.OBJ) drivers, 
  237. VIPX will automatically read the configuration of the NIC from the 
  238. driver and virtualize the selected IRQs.
  239.  
  240. The only time that VIPX cannot automatically detect the LAN IRQs is
  241. when using the IBM LAN Support Program with either SLANSUP.OBJ or 
  242. LANSUP.COM.  In this case, the LAN IRQ is not readable from the
  243. driver.  The reason (so I am told) is that IBM LAN Support does not
  244. have an API to read the IRQ setting on the NIC:  The only way to get
  245. this information is to read the NIC hardware itself.  The problem
  246. with doing this is that the hardware can be Token Ring, PCN2 or
  247. Ethernet.  This means that VIPX would have to be aware of all of the
  248. different configurations of all of these hardwares. Instead of this,
  249. VIPX requires the IBM LAN Support user to specify the NIC IRQ in the
  250. [VIPX] section of the SYSTEM.INI.  IRQs range from 0 to F (hex).  An
  251. example is listed below:
  252.  
  253. [VIPX]
  254. VirtualizeIrq2=TRUE
  255. VirtualizeIrq3=TRUE
  256.  
  257. In this example, VIPX will virtualize both IRQ 2 and IRQ 3.  VIPX can
  258. virtualize upto 4 different LAN IRQs.  The reason for virtualizing 
  259. multiple IRQs is to allow other LAN cards and protocols to be
  260. installed on the same PC and prevent them from deadlocking the
  261. machine.  For example, you may have IPX running through an NE2000
  262. card on IRQ 3 and TCP/IP running through to an IBM Token Ring card on
  263. IRQ 2.
  264.  
  265. One side note.  On an AT class PC, IRQ 2 is is really IRQ 9 on the
  266. physical machine.  IRQ 2 is a special cascade interrupt used to
  267. access IRQs 9-F.  Whenever a NIC raises the IRQ 2 line, the hardware
  268. configuration on the PC's bus actually issues an IRQ 9.  Also, if a
  269. NIC is configured to IRQ 2, the LAN driver during its initialization
  270. procedure will detect that the PC is an AT class machine and will
  271. correctly configure its software to use IRQ 9.
  272.  
  273. Getting to the point, the Windows VPICD VxD will not allow IRQ 2 to
  274. be exclusively virtualized by any VxD.  However, when VIPX detects
  275. that IRQ 2 is to be virtualized, it changes the IRQ to 9.  So if you
  276. are using the IBM LAN Support Program on an IBM Token Ring NIC set to
  277. IRQ 2, you should have not trouble.  The following two configurations
  278. are equivalent:
  279.  
  280. [VIPX]
  281. VirtualizeIrq2=TRUE
  282.  
  283. or
  284.  
  285. [VIPX]
  286. VirtualizeIrq9=TRUE
  287.  
  288. One important note when using VIPX 1.15 or higher:  Set the
  289. TimerCriticalSection under the [386Enh] section in the SYSTEM.INI as
  290. follows:
  291.  
  292. [386Enh]
  293. TimerCriticalSection=10000
  294.  
  295. The reason for this is that the timer interrupt can cause a deadlock
  296. with the virtualization of the the LAN interrupt.
  297.  
  298. You should note that the VirtualizeIrq[0-F] can be used to override
  299. any auto-virtualization of LAN IRQs.  For example, if your ODI LAN 
  300. driver is configured for IRQ 5, VIPX will automatically detect that
  301. IRQ 5 is to be virtualized.  However, if you have
  302.  
  303. [VIPX]
  304. VirtualizeIrq5=FALSE
  305.  
  306. in your SYSTEM.INI, VIPX will override the automatic detection of IRQ
  307. 5 and not virtualize the interrupt.
  308.  
  309. Another way to disable the auto-virtualization feature of VIPX is
  310. with the VpicdFix or AutoIrqVirtualize parameters (See III.B.3. 
  311. VpicdFix or III.B.4 AutoIrqVirtualize).
  312.  
  313.  
  314. III.B.6.  VipxErrorMessages
  315. ---------------------------
  316. III.B.7.  VipxWarningMessages
  317. -----------------------------
  318. III.B.8.  VipxOutDebugStrOnErrors
  319. ---------------------------------
  320. III.B.9.  VipxOutDebugStrOnWarnings
  321. -----------------------------------
  322. III.B.10. VipxBreakOnErrors
  323. ---------------------------
  324. III.B.11. VipxBreakOnWarnings
  325. -----------------------------
  326.  
  327. These parameters are only enabled in Beta versions of VIPX. With Beta
  328. software, the user can enable/disable these parameters to make VIPX
  329. print error/warning messages to the user screen and debugger screen.
  330. They can also instruct VIPX to execute a break point whenever an
  331. error/warning occurs so that debugging can be done on site.
  332.  
  333.  
  334. IV.  Programming to the VIPX Interface
  335. --------------------------------------
  336.  
  337. IV.A.  Getting the VIPX API Entry Point
  338. ---------------------------------------
  339.  
  340. VIPX supports API calls from 16-bit DOS and Windows applications. 
  341. This is the interface which NWIPXSPX.DLL uses to issue calls to VIPX.
  342.  
  343. To get the VIPX Device V86/PM API Entry Point, a 16-Bit DOS/Windows 
  344. application must do the following:
  345.  
  346.     DWORD VIPX_API_ADDR;
  347.     #define WIN_GET_DEV_API_ENTRY_PT 0x1684
  348.     #define VIPX_DEVICE_ID 0x0200
  349.     GetVipxApiAddr ()
  350.     {
  351.         _asm
  352.         {
  353.             mov     ax, WIN_GET_DEV_API_ENTRY_PT
  354.             mov     bx, VIPX_DEVICE_ID
  355.             int     2fh
  356.             mov     word ptr VIPX_API_ADDR+2, ES
  357.             mov     word ptr VIPX_API_ADDR, DI
  358.         }
  359.         if (!VIPX_API_ADDR)
  360.         {
  361.             print error - NO PM API! RIGHT VERS OF VIPX?
  362.             handle error
  363.         }
  364.     }
  365.  
  366. Once the API address is obtained, an application then can make a far
  367. call to the VIPX API Handler to do two primary functions:
  368.  
  369.     1.  Request an 16-Bit PM IPX/SPX operation (SendPacket, etc...).
  370.     2.  Request a device specific operation (GetVersion, GetDiagnotics).
  371.  
  372. The way that VIPX differentiates between the two types of APIs is by
  373. the value in the BH register.  If BH equals 00h, then VIPX will
  374. handle a 16-Bit Protected Mode IPX/SPX operation.  NOTE:  The V86
  375. IPX/SPX API interface (i.e. DOS application) is done through the
  376. globally loaded IPX TSR itself.  This way, DOS applications do not
  377. need to change the way they access the IPX/SPX APIs under Windows and
  378. DOS.
  379.  
  380. If BH equals the character 'V' (56h), then VIPX will will perform a
  381. V86/PM Device Specific API.  These APIs are listed below (and will be
  382. presented in full specification later):
  383.  
  384.     GetVipxVersion
  385.     GetVipxDiagnostics
  386.  
  387. For IPX/SPX operations (BH = 00h), a 16-bit Windows program must
  388. follow the IPX/SPX API specification in the NetWare System Calls-DOS
  389. manual under the Communications chapter.  Coverage of the DOS IPX/SPX
  390. APIs is beyond the scope of this document.  However, an example is
  391. provided below for reference:
  392.  
  393.     DWORD VIPX_API_ADDR;
  394.     int ccode;
  395.     CallPmIpxSpxApi ()
  396.     {
  397.         _asm
  398.         {
  399.             mov     bx, (IPX operation number)
  400.             (set other registers as specified by the function)
  401.             call    VIPX_API_ADDR
  402.             (save return registers as specified by the function)
  403.             ErrorCondition = (function error condition)
  404.         }
  405.         if (ErrorCondition)
  406.         {
  407.             print error - function failed!
  408.             handle error
  409.         }
  410.     }
  411.  
  412.  
  413. IV.B.  Memory Management Issues
  414. -------------------------------
  415.  
  416. One problem with using the IPX/SPX interface in Windows applications
  417. is that all memory used by VIPX must be page-locked.  Since
  418. VIPX is an asynchronous driver, all application ECBs, fragments and
  419. ESRs must be in locked memory to insure that they can be accessed
  420. during interrupt time.  If an ECB, fragment or ESR is in moveable
  421. memory, it could move when VIPX "owns" that memory.  For example,
  422. assume an ECB is located at 16-bit PM address 0A10:0000 in moveable
  423. memory.  Assume this address tranlates to 32-bit linear address
  424. 8140ACB0.  Assume also that the application gives the ECB to VIPX and
  425. VIPX commences to work on it.  Now if Windows does garbage
  426. collection, the linear mapping for the 16-bit address 0A10:0000 may
  427. change from 8140ABC0 to 81408100.  When VIPX tries to access the old
  428. linear address 8140ABC0, memory corruption will occur because VIPX is
  429. accessing the wrong memory.
  430.  
  431. To avoid memory corruption, follow the guidelines below (your
  432. comments on the effectiveness of these guidelines are appreciated):
  433.  
  434. 1.  Read Chapter 15 (Memory Management) and 16 (More Memory
  435. Management) of the MS Windows SDK Version 3.1.  These will explain
  436. many terms and concepts which will be discussed below.
  437.  
  438. 2.  Define all ESRs, routines called by ESRs and data accessed by
  439. ESRs in one module.  In the .DEF file for your project, define the
  440. module's code and data segments as FIXED.
  441.  
  442. 3.  Use GlobalPageLock() on all dynamically allocated heap memory
  443. (via GlobalAlloc() or LocalAlloc()) which is to be used by VIPX or an
  444. ESR.  GlobalPageLock() informs the GUI kernel memory manager to fix
  445. the linear base of the selector until a GlobalPageUnlock() is done on
  446. the memory.
  447.  
  448. 4.  When using GlobalAlloc(), use the GMEM_DDESHARE and GMEM_FIXED
  449. options.  GMEM_DDESHARE will allocate the memory (so I am told) high
  450. up on the global heap.  This will avoid using conventional memory and
  451. will allow room for other Windows applications to load after your
  452. application has been loaded and initialized.  The GMEM_FIXED option
  453. tells the GUI kernel memory manager that the memory is FIXED.  This
  454. does not mean that the linear address will remain unchanged, but it
  455. does mean that the segmented address (selector:offset) will not
  456. change.  After the call to GlobalAlloc(), call GlobalLock() to
  457. derefernce the global memory handle.  This provides a far pointer
  458. (selector:offset) to the memory which will not change.  Next call
  459. GlobalPageLock() to fix the linear base of the selector so that the
  460. 32-bit linear mapping of the selector:offset does not change.
  461.  
  462. 5.  If local moveable or discardable code or data segments are used
  463. for ECBs, fragments or ESRs, use GlobalPageLock() on those segments.
  464.  
  465. 6.  After you load your application and then you get a message from
  466. Windows that there are not enough resources to load another program,
  467. try changing the attributes of your segments. I believe this
  468. condition occurs because too much conventional memory has been
  469. allocated to fixed memory objects and Windows needs to allocate
  470. conventional memory to load a program.  One strategy may be to do a
  471. GlobalCompact() with a argument of -1 at initialization time.  This
  472. will move all moveable segments and discard all discardable
  473. segments.  Next call GlobalAlloc() for one large moveable global
  474. object which contains all of your ECBs, fragments and data which must
  475. be accessed from VIPX or your ESRs.  Next call GlobalLock() to
  476. dereference the global handle.  Last call GlobalPageLock() to fix the
  477. linear address of the object.  (I have not tried this yet, so there
  478. may be some problems with it).  Also make your ESR code as small as
  479. possible using PostMessage to indicate to your application that an
  480. ECB has completed.  This way, your FIXED code segment will have
  481. little impact on conventional memory.
  482.  
  483. 7.  Read Chapter 4 of The Windows Programmer's Guide to DLLs and
  484. Memory Management by Mike Klein.
  485.  
  486.  
  487. IV.C.  APIs Specific to VIPX.386
  488. --------------------------------
  489.  
  490. There are two APIs via the VIPX API entry point which are specific
  491. to VIPX.386 only.  These are:
  492.  
  493.     GetVipxVersion
  494.     GetVipxDiagnostics
  495.  
  496. The device specific APIs are called in the same manner as the IPX/SPX
  497. APIs except that BH = 56h.  Some sample code is listed below:
  498.  
  499.     DWORD VIPX_API_ADDR;
  500.     #define VIPX_DEVICE_API_TAG 'V'
  501.     int ccode;
  502.     CallVipxDeviceSpecificApi ()
  503.     {
  504.         _asm
  505.         {
  506.             mov     bh, VIPX_DEVICE_API_TAG
  507.             mov     bl, api_function_number
  508.             (set other registers for API function)
  509.             call    VIPX_API_ADDR
  510.             mov     ccode, ax
  511.         }
  512.         if (!ccode)
  513.         {
  514.             print error - api function failed!
  515.             handle error
  516.         }
  517.     }
  518.  
  519. The full API specification for GetVipxVersion is as follows:
  520.  
  521. GetVIPXVersion
  522.  
  523.     This procedure puts the VIPX major and minor version numbers into
  524.     the client AH and AL.
  525.  
  526.     To call this API a DOSBOX or Windows 3.0 application
  527.     must first get the VIPX Device API entry point (see
  528.     VIPX_V86_API_Handler or VIPX_PM_Handler).  Then
  529.     it must do the following:
  530.  
  531. Example:
  532.  
  533.     DWORD VIPX_API_ADDR;
  534.     BYTE VIPX_Major_Version;
  535.     BYTE VIPX_Minor_Version;
  536.     #define VIPX_DEVICE_API_TAG 'V'
  537.     #define VIPX_GET_VERSION 0x00
  538.     int ccode;
  539.     main ()
  540.     {
  541.         _asm
  542.         {
  543.             mov     bh, VIPX_DEVICE_API_TAG
  544.             mov     bl, VIPX_GET_VERSION
  545.             call    VIPX_API_ADDR
  546.             mov     ccode, ax
  547.             mov     VIPX_Major_Version, bh
  548.             mov     VIPX_Minor_Version, bl
  549.         }
  550.         if (!ccode)
  551.         {
  552.             print error - api function failed!
  553.             handle error
  554.         }
  555.     }
  556.  
  557. Entry:
  558.     BH      056h - VIPX Device API
  559.     BL      0 - Get VIPX Version function
  560.  
  561. Exit:
  562.     BH      VIPX Major Version
  563.     BL      VIPX Minor Version
  564.     AX      Return code
  565.             0000h - success
  566.  
  567. Uses:
  568.     AX, BX, Flags
  569.  
  570. The full API specification for GetVIPXDiagnostics is as follows:
  571.  
  572. GetVIPXDiagnostics
  573.  
  574.     This procedure puts the diagnostics for VIPX into the
  575.     specified client diagnostic buffer.  The buffer is 112 bytes 
  576.     and has the following structure:
  577.  
  578.         VIPXDiagStruc   STRUC
  579.             MallocCount                     dd      ?
  580.             MallocErrorCount                dd      ?
  581.             FreeCount                       dd      ?
  582.             AllocPageCount                  dd      ?
  583.             FreePageCount                   dd      ?
  584.             PMRequestCount                  dd      ?
  585.             TSRServiceCount                 dd      ?
  586.             ServiceAESEventCount            dd      ?
  587.             ServiceIPXEventCount            dd      ?
  588.             PostEventCount                  dd      ?
  589.             IPXGetECBCount                  dd      ?
  590.             IPXGetECBBadPacketCount         dd      ?
  591.             IPXGetECBOutOfResource          dd      ?
  592.             IPXCountECBCount                dd      ?
  593.             IPXReturnECBCount               dd      ?
  594.             IPXRequestCount                 dd      ?
  595.             FreeECBCount                    dd      ?
  596.             AllocECBCount                   dd      ?
  597.             LockECBCount                    dd      ?
  598.             UnlockECBCount                  dd      ?
  599.             OpenSocketCount                 dd      ?
  600.             CloseSocketCount                dd      ?
  601.             SendPacketCount                 dd      ?
  602.             ListenCount                     dd      ?
  603.             IPXScheduleEventCount           dd      ?
  604.             AESScheduleEventCount           dd      ?
  605.             CancelECBCount                  dd      ?
  606.             PMInt2FCount                    dd      ?
  607.             AllocPageErrorCount             dd      ?
  608.             FreePageErrorCount              dd      ?
  609.             FreeECBErrorCount               dd      ?
  610.             MaxVipxAllocSize                dd      ?
  611.             MaxVipxFragsSize                dd      ?
  612.             TsrServiceReenterCnt            dd      ?
  613.             IPXGetEcbSktNotOpenCnt          dd      ?
  614.             IPXGetEcbNoEcbAvailCnt          dd      ?
  615.         ENDS
  616.  
  617.     NOTE:  Starting with VIPX 1.16, MaxVipxAllocSize, MaxVipxFragsSize, and
  618.     TsrServiceReenterCnt have been updated to DWORDs.  Also IPXGetEcbSktNotOpenCnt
  619.     and IPXGetEcbNoEcbAvailCnt have been added.
  620.  
  621.  
  622.     To call this API a DOSBOX or Windows 3.0 application
  623.     must first get the VIPX Device API entry point (see
  624.     VIPX_V86_API_Handler or VIPX_PM_Handler).  Then
  625.     it must do the following:
  626.  
  627. Example:
  628.  
  629.     DWORD VIPX_API_ADDR;
  630.     VIPXDiagStruc Diag;
  631.     #define VIPX_DEVICE_API_TAG 'V'
  632.     #define VIPX_GET_DIAGNOSTIC 0x01
  633.     int ccode;
  634.     main ()
  635.     {
  636.         _asm
  637.         {
  638.             mov     bh, VIPX_DEVICE_API_TAG
  639.             mov     bl, VIPX_GET_DIAGNOSTIC
  640.             mov     ax,ds
  641.             mov     es,ax
  642.             mov     di, offset Diag
  643.             mov     cx, size VIPXDiagStruc
  644.             call    VIPX_API_ADDR
  645.             mov     ccode, ax
  646.         }
  647.         if (!ccode)
  648.         {
  649.             print error - api function failed!
  650.             handle error
  651.         }
  652.     }
  653.  
  654. Entry:
  655.     BH      056h - VIPX Device API
  656.     BL      1 - Get VIPX Diagnostics
  657.     ES:SI   ptr to client VIPX Diagnostics buffer.
  658.     CX      Length of diagnostics buffer in bytes
  659.  
  660. Exit:
  661.     ES:SI   client's buffer now has VIPX diags.
  662.     AX      Return code
  663.             0000h - success
  664.             0FFFEh - diagnostics buffer too short
  665.  
  666. Uses:
  667.     AX, SI, ES, Flags
  668.  
  669.  
  670. IV.D.  Handling Reentrancy Issues
  671. ---------------------------------
  672.  
  673. A Windows application which uses the IPX/SPX APIs via VIPX must
  674. handle reentrancy issues.  Since the IPX/SPX API is interrupt driven,
  675. any code running while interrupts are enabled can (and will) be
  676. interrupted at any time by IPX/SPX.  When an IPX event completes, an
  677. ESR for that event is called.  The ESR is entered with interrupts
  678. disabled.  If that ESR reenables interrupts, then it can be
  679. reentered.  This is usually a problem if:
  680.  
  681. 1.  The application has limited stack space
  682. 2.  The application's ESR uses global variables
  683.  
  684. An ESR must protect itself from these two reentrancy issues if it
  685. reenables interrupts.  Interrupts can be reenabled by several
  686. different places.  One of the most notorious is the PostMessage()
  687. Windows call.  Any ESR using PostMessage() must be reentrant. 
  688. Another notorious enabler of interrupts is IPXSendPacket. 
  689. IPXSendPacket will call the LSL to transmit a packet.  The LSL,
  690. however,  will reenable interrupts during transmission of a packet. 
  691. This can cause reentrancy into any ESR that calls IPXSendPacket.
  692.  
  693. An ESR can handle reentrancy issues by
  694.  
  695. 1.  Switching to a stack exclusively for the ESR to use.
  696. 2.  Provide a reetrancy queue.
  697.  
  698. The stack switching is usually done in assembly.  It is by far the
  699. easiest and most efficient way to protect your stack.  The code for
  700. stack switching is listed below:
  701.  
  702. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  703. memL equ 1 ; large model
  704. ?PLM = 1 ; pascal conventions
  705. ?WIN = 1 ; epilogue/prolog code
  706.  
  707.  
  708. INCLUDE cmacros.inc
  709.  
  710. sBegin  DATA
  711.     assumes DS, DATA
  712.  
  713. ReenterCnt      db      0
  714.  
  715. OldSs           dw      0
  716. OldSp           dw      0
  717.  
  718. EsrStack        db      4096 dup (0)
  719.  
  720. EndEsrStack label byte
  721.  
  722. sEnd    DATA
  723.  
  724. sBegin  CODE
  725.     assumes CS, CODE
  726. ;       
  727. ;       This is the front-end procedure to the C function "ReceiveESR"
  728. ;
  729.  
  730. externFP cESR
  731. PUBLIC aESR
  732. aESR    proc FAR
  733.  
  734.     nop
  735.     nop
  736.     nop
  737.     nop
  738.  
  739.     ; Don't know why Windows needs to inc bp
  740.     inc     bp
  741.     push    bp
  742.     mov     bp, sp
  743.  
  744.     ; Save data segment
  745.     push    ds
  746.  
  747.     ; Fix to our data segmented
  748.     mov     ax, DGROUP
  749.     mov     ds, ax
  750.  
  751.     ; Update our reenter cnt
  752.     inc     ReenterCnt
  753.  
  754.     ; Do we need to switch stack to ESR stack?
  755.     cmp     ReenterCnt, 2           ;are we reentering?
  756.     jae     CallEsr                 ;y: call esr
  757.     mov     ax, ss
  758.     cmp     ax, DGROUP              ;same stack seg?
  759.     jne     SwitchStack             ;n: switch
  760.     lea     ax, EsrStack            ;ax=offset of EsrStack
  761.     cmp     sp, ax                  ;below the esr stack?
  762.     jb      SwitchStack             ;y: switch
  763.     lea     ax, EndEsrStack         ;ax=offset of end of EsrStack
  764.     cmp     sp, ax                  ;above the esr stack
  765.     jae     SwitchStack             ;y: switch
  766.  
  767.     ; Call the esr
  768. CallEsr:
  769.     push    es
  770.     push    si
  771.     cCall   cESR
  772.     lea     sp, [bp-2]
  773.  
  774.     ; Update reenter cnt
  775.     dec     ReenterCnt
  776.  
  777.     ; Switch stacks back?
  778.     jz      SwitchStackBack
  779.  
  780.     ; return to IPX
  781. EsrExit:
  782.  
  783.     ; restore ds and bp
  784.     pop     ds
  785.     pop     bp
  786.     dec     bp
  787.  
  788.     ret
  789.  
  790. SwitchStack:
  791.     mov     ax, sp
  792.     mov     OldSp, ax
  793.     mov     ax, ss
  794.     mov     OldSs, ax
  795.     lea     ax, EndEsrStack
  796.     mov     sp, ax
  797.     mov     ax, ds
  798.     mov     ss, ax
  799.     jmp     CallEsr
  800.  
  801. SwitchStackBack:
  802.     mov     ax, OldSp
  803.     mov     sp, ax
  804.     mov     ax, OldSs
  805.     mov     ss, ax
  806.     jmp     EsrExit
  807.  
  808. aESR    endp
  809.  
  810. sEnd    CODE
  811.     end
  812.  
  813. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  814.  
  815. The above code switched to a 4K stack before calling the main ESR
  816. code.  Notice that there is a reentrancy count to determine if a stack
  817. switch should occur.  This is used to prevent switching the stack
  818. again if the ESR is reentered.
  819.  
  820. The main ESR code is written in C.  An example is listed below:
  821.  
  822. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  823. void far pascal cESR(ECB far *esr_ecb)
  824. {
  825.     static ECB FAR * EcbQueue;
  826.     static ECB FAR * EcbQueueTail = (ECB FAR *)&EcbQueue;
  827.     static WORD ReenterFlag = 0;
  828.  
  829.     // Check for reentrancy
  830.     if (ReenterFlag)
  831.     {
  832.         // Enqueue the ECB
  833.         EcbQueueTail->Link = (void far *)esr_ecb;
  834.         EcbQueueTail = (ECB far *)esr_ecb;
  835.         esr_ecb->Link = NULL;
  836.         return;
  837.     }
  838.  
  839.     // Set the reentrany flag to indicate that we can't allow 
  840.     // another thread through the ESR.
  841.     ReenterFlag++;
  842.  
  843.     // Process the current ECB
  844. NextEcb:
  845.  
  846.     // Do some work here if need be -- BUT BE QUICK!
  847.     ...
  848.  
  849.     // Post a message to the Windows App that the event happened
  850.     PostMessage (
  851.         hMainWnd, 
  852.         USER_DEFINED_IPX_EVENT_HAPPPENED, 
  853.         0, 
  854.         (DWORD) esr_ecb);
  855.  
  856.     // Check for anything on the queue
  857. CheckQueue:
  858.     if (EcbQueue)
  859.     {
  860.         esr_ecb = EcbQueue;
  861.         EcbQueue = (ECB FAR *)(EcbQueue->Link);
  862.         if (!EcbQueue)
  863.             EcbQueueTail = (ECB FAR *)&EcbQueue;
  864.         goto NextEcb;
  865.     }
  866.  
  867.     // Reset the reentrancy flag to indicate that the 
  868.     // first thread is done
  869.     ReenterFlag--;
  870. }
  871.  
  872. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  873.  
  874. One side note about the code above.  PostMessage() will queue a
  875. message onto an applications message queue.  This queue is limited in
  876. size.  If the application is fairly LAN I/O intensive, the message
  877. queue can be overrun.  In this case, PostMessage() will simply drop 
  878. the message.  If this happens, your application will never get
  879. notified of the event.  To insure that messages are never dropped,
  880. you may want to increase the size of your message queue.  Another
  881. alternative is to have the message indicate that an event happened
  882. and the message loop will then poll all ECBs to determine which ones
  883. completed.  This way, if a message is dropped, it won't matter:  the
  884. main message loop will still detect that the ECB completed and will
  885. be able to reuse the ECB.
  886.  
  887.  
  888.  
  889. IV.E.  Using NWIPXSPX.DLL
  890. -------------------------
  891.  
  892. Most Windows applications will use the IPX/SPX APIs via the
  893. NWIPXSPX.DLL.  The reason is that NWIPXSPX.DLL address the issues for
  894. both Standard and Enhanced mode Windows.  Under Standard mode,
  895. NWIPXSPX will use TBMI2.COM to virtualize the local ECBs passed to
  896. it.  Under Enhanced mode, NWIPXSPX.DLL will use VIPX.386 to
  897. virtualize ECBs.  If your Windows application is targeted toward
  898. Enhanced mode only, then you may find it more convenient to program
  899. directly to the VIPX interface in assembly.  However, if you are
  900. interested in using an established C interface for IPX/SPX, then
  901. NWIPXSPX.DLL is the way to go.  There is example code using NWIPXSPX.DLL 
  902. on the Compuserve NOVDEV forum.
  903.  
  904.  
  905. V.  Global DOS TSRs using IPX/SPX
  906. ---------------------------------
  907.  
  908. TSRs loaded before Windows can call IPX without the intervention of
  909. VIPX.  This is done by ORing the BX register with 8000h (i.e. setting
  910. the high-bit of the function number).  This is a previously
  911. undocumented feature in IPX to support the globally loaded NETX
  912. driver.  When IPX sees that a request has the high-bit set, it will
  913. assume that the request came from a globally loaded TSR and will not
  914. forward the request to VIPX.
  915.  
  916. The only problem with the high-bit is that SPX does not recognize
  917. it.  When SPX gets a request with a high-bit, SPX ignores it and
  918. reposts the ECB to IPX without the preserving the high-bit.  VIPX
  919. will then get the IPX request and then determine if the ECB is really
  920. loaded in global memory.  If so, then it will simply pass the global
  921. request back down to IPX with the high-bit set.
  922.  
  923.  
  924.